
#include "websocket.h"
#include <thread>
#include "win.h"
#include "util.h"
#include "string"
#include "zlib.h"
#include "base64.h"
#include "looper.h"
#include "rapidjson/writer.h"
#include "rapidjson/error/en.h"
#include "rapidjson/error/error.h"
#include "rapidjson/stringbuffer.h"
#include "JsonReader.h"
#include "rapidjson/rapidjson.h"

#include <map>
volatile bool runing = false;

Looper *looper;
// user_data_context_t context;

std::mutex saveresult_mutex;
std::condition_variable saveresult_cond;

std::map<uint64_t, session_t *> serverUUID_map;
std::map<uint64_t, int> uuid_result_map;

void app_init(designreport_t designreport, printreport_t printreport, showreport_t showreport, exportpdf_t exportpdf)
{
  dhs_designreport = designreport;
  dhs_printreport = printreport;
  dhs_showreport = showreport;
  dhs_exportpdf = exportpdf;

  Looper loop;

  looper = &loop;
  std::thread([&]()
              { runApp(); })
      .detach();

 std::thread([&]()
              { topWindow(); })
      .detach();
  loop.run();
}

user_data_context_t *allocate_user_data_context()
{
  user_data_context_t *context = (user_data_context_t *)malloc(sizeof(user_data_context_t));
  return context;
}

void free_user_data_context(user_data_context_t *context)
{
  if (context != nullptr)
  {
    free(context);
  }
}

bool sendClientJson(const char *type, const char *data, user_data_context_t *userprivate)
{
  BEGIN_EXCEPT
  rapidjson::StringBuffer buffer;
  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  writer.StartObject();
  writer.Key(data_type);
  writer.String(type);
  writer.Key(data_token);
  writer.String(data);
  writer.Key(uuid_token);
  writer.Uint64(userprivate->uuid);
  writer.Key(server_uuid_token);
  writer.Uint64(userprivate->server_uuid);
  writer.EndObject();

  DHS_LOG("sendClientJson======begin===\n");

  auto *ws = serverUUID_map[userprivate->server_uuid];
  if (ws == nullptr)
  {
    DHS_LOG("sendClientJson====ws notfound==\n");
    return false;
  }
  auto status = ws->send(buffer.GetString(), uWS::OpCode::TEXT);

  DHS_LOG("sendClientJson=====%i,%s\n", status,buffer.GetString());

  if (status == session_t ::SUCCESS)
  {
    return true;
  }
  else
  {
    return false;
  }
  END_EXCEPT
  return false;
}
bool dhs_cb(const char *data, user_data_context_t *userprivate)
{
  std::unique_lock<std::mutex> lock(saveresult_mutex);
  auto b = sendClientJson(type_save_call, data, userprivate);

  if (!b)
  {
    DHS_LOG("sendClientJson false \n");

    return false;
  }

  auto state = saveresult_cond.wait_for(lock, std::chrono::seconds(10));
  DHS_LOG("=========================================3\n");

  if (state == std::cv_status::timeout)
  {
    DHS_LOG("dhs_cb==end===timeout=\n");
    return false;
  }
  else
  {

    int result = uuid_result_map[userprivate->uuid];
    uuid_result_map.erase(userprivate->uuid);

    DHS_LOG("dhs_cb====end===%i\n", result);

    if (result == 0)
    {
      return false;
    }
    else
    {
      return true;
    }
  }
}

void proccessMessage(session_t *ws, std::string_view msg, user_data_context_t *context)
{
  DHS_LOG("proccessMessage=============begin\n");

  rapidjson::Document doc;
  if (doc.Parse(msg.begin()).HasParseError())
  {
    DHS_LOG("proccessMessage====error==%s\n", rapidjson::GetParseError_En(doc.GetParseError()));

    return;
  }

  const char *dataType = doc[data_type].GetString();

  const char *cmd = doc[command_token].GetString();

  if (doc.HasMember(urlList_token))
  {
    rapidjson::Value &urllist = doc[urlList_token];

    dhs_downloadFile(urllist);
  }

  DHS_LOG("proccessMessage====dataType==%s,%s\n", dataType,cmd);

  if (strcmp(c_dhs_image_dir, cmd) == 0)
  {

    sendClientJson(type_save_call, getImagePath(), context);
    return;
  }

    if (!doc.HasMember(template_token))
  {
    return;
  }

  if (!doc.HasMember(jsonDbData_token))
  {
    return;
  }
  
  const char *dbData = doc[jsonDbData_token].GetString();

  const char *tmpl = doc[template_token].GetString();
   if (strcmp(c_dhs_design, cmd) == 0)
  {
    dhs_designreport(dbData, tmpl, dhs_cb, context);
  }
  else if (strcmp(c_dhs_print, cmd) == 0)
  {

    int _copys = 1;

    if (doc.HasMember(copys_token))
    {
      rapidjson::Value &copys = doc[copys_token];
      _copys = copys.GetInt();
    }
   DHS_LOG("c_dhs_print1=====\n");

    dhs_printreport(dbData, tmpl, _copys);
   DHS_LOG("c_dhs_print=====\n");

  }
  else if (strcmp(c_dhs_show, cmd) == 0)
  {
    dhs_showreport(dbData, tmpl);
  }
  else if (strcmp(c_dhs_exportpdf, cmd) == 0)
  {
    int _pdfbase64 = 1;

    dhs_exportpdf(dbData, tmpl, _pdfbase64, dhs_cb, context);
  }
}

void runApp()
{
  // LARGE_BUFFER_SIZE
  uWS::App().get("/*", [](auto *res, auto *)
                 { res->end("Hello dhs"); })
      .listen("127.0.0.1",60706, [](auto *listen_socket)
              {
	    if (listen_socket) {
			std::cout << "Listening on port " << 60706 << std::endl;
	    } })
      .ws<uint64_t>("/dhs", {.compression = uWS::DISABLED,
                             .maxPayloadLength = 1024 * 1000 * 5,
                             .maxBackpressure = 1024 * 1000 * 5,
                             .closeOnBackpressureLimit = true,
                             .open = [](session_t *ws)
                             { DHS_LOG("open====ws=====\n"); },
                             .message = [](auto *ws, std::string_view message, uWS::OpCode opCode)
                             {
                              // printf("onmessage\n");
                               if (opCode != uWS::OpCode::TEXT)
                               {
                                  DHS_LOG("error message must be text\n");
                                 return;
                               }

                               char *msg = new char[message.length() + 1];
                               memcpy(msg, message.begin(), message.length());
                               msg[message.length()] = '\0';
                               user_data_context_t *context =nullptr;

                               BEGIN_EXCEPT
                               //  MyHandler handler;
                               //  Reader reader;
                               //  StringStream ss(msg);
                               //  reader.Parse(ss, handler);

                               rapidjson::Document doc;
                               doc.Parse(msg);

                               if(doc.HasParseError()){
                                 DHS_LOG("json parse error \n");
                                break;
                               }

                               if (!doc.HasMember(server_uuid_token) || !doc.HasMember(data_type))
                               {
                                 DHS_LOG("%s,%s params not fond\n", server_uuid_token, data_type);

                                 break;
                               }
        


                               // reader.IterativeParseInit();
                               // while (!reader.IterativeParseComplete()) {
                               //     reader.IterativeParseNext<kParseDefaultFlags>(ss, handler);
                               // Your handler has been called once.
                               // }

                          const char * dataType= doc[data_type].GetString() ;  

                           uint64_t  serverUUID= doc[server_uuid_token].GetUint64();
                           serverUUID_map[serverUUID] = ws;

                             

                              if (strcmp(dataType, type_connect) == 0)
                               {

                                 break;
                               }
                            
                              if (!doc.HasMember(uuid_token))
                               {
                                DHS_LOG("uuid not fond\n");
                                 break;
                               }

                              uint64_t uuid= doc[uuid_token].GetUint64();

                              uint64_t *serveruuid_addr = ws->getUserData();
                              (*serveruuid_addr) = serverUUID;
                               context= allocate_user_data_context();
                               context->server_uuid = serverUUID;
                               context->uuid = uuid;

                                //  DHS_LOG("onmessage===== \n");
                               if (strcmp(dataType, type_return_status) == 0)
                               {

                                 //  DHS_LOG("type_return_status \n");
                                 uuid_result_map[uuid] = doc[data_token].GetInt();
                                 saveresult_cond.notify_all();
                                 break;
                                  }


                               if (strcmp(dataType, type_command) != 0)
                               {
                                DHS_LOG("command not fond\n");
                                break;
                               }
                               if (!doc.HasMember(command_token))
                               {
                                 DHS_LOG("command_token not fond\n");
                                 break;
                               }

                               const char *cmd = doc[command_token].GetString();

                               if (strcmp(c_dhs_print, cmd) != 0 && strcmp(c_dhs_exportpdf, cmd) != 0)
                               {

                                 if (runing)
                                 {

                                   DHS_LOG("command_runing \n");

                                   sendClientJson(type_error, "报表已打开。。。", context);
                                   break;
                                 }
                                 runing = true;
                               }

                                 looper->postmessage({[&ws, &msg,serverUUID,uuid]()
                                                      {
                                                        user_data_context_t *context = allocate_user_data_context();
                                                        context->server_uuid = serverUUID;
                                                        context->uuid = uuid;
                                                        BEGIN_EXCEPT

                                                        proccessMessage(ws, {msg, strlen(msg)}, context);

                                                        END_EXCEPT
                                                        runing = false;
                                                        uuid_result_map.erase( context->uuid);
                                                        free_user_data_context(context);

                                                        sendClientJson(type_command_end, "", context);
                                                      }});
                               
                               END_EXCEPT

                               free_user_data_context(context); },
                             .drain = [](auto *ws)
                             { DHS_LOG("drain================\n"); },
                             .close = [](auto *ws, int code, std::string_view message)
                             {
                         
                          uint64_t * serveruuid=   ws->getUserData();

                          DHS_LOG("webserver close begin,%d\n", serverUUID_map.size());
                          serverUUID_map.erase(*serveruuid);

                          DHS_LOG("webserver close end,%d\n", serverUUID_map.size()); }

                            })
      .run();
}